package cn.wolfcode.web.controller;

import cn.wolfcode.common.domain.UserInfo;
import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CommonCodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.common.web.anno.RequireLogin;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.SeckillProduct;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMQResult;
import cn.wolfcode.mq.OrderMessage;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.DateUtil;
import cn.wolfcode.util.UserUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpRequest;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import rx.internal.schedulers.NewThreadWorker;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("/order")
@Slf4j
public class OrderInfoController {
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IOrderInfoService orderInfoService;

    public static ConcurrentHashMap<Long,Boolean> IS_USE_OUT = new ConcurrentHashMap<>();

    @GetMapping("find")
    @RequireLogin
    public Result<OrderInfo> find(String orderNo,HttpServletRequest request) {
        // 参数校验
        if (StringUtils.isEmpty(orderNo)) {
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }

        OrderInfo orderInfo = orderInfoService.findByOderNo(orderNo);
        if (orderInfo == null){
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }

        // 不是当前用户的订单无法查看
        UserInfo user = UserUtil.getUser(redisTemplate, request.getHeader("token"));
        if ( !orderInfo.getUserId().equals(user.getPhone()) ) {
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }

        return Result.success(orderInfo);
    }

    @PostMapping("doSeckill")
    @RequireLogin
    public Result<String> doSeckill(Long seckillId, int time, HttpServletRequest request){

        // 1.判断用户是否登录 => 贴注解
        // 2.校验参数合法性
        if (seckillId == null || seckillId <= 0 || time < 0) {
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        // 3.校验是否在秒杀时间范围
//        Date date = new Date();
//        boolean isLegalTime = DateUtil.isLegalTime(date, time);
//        if (!isLegalTime){
//            throw new BusinessException(SeckillCodeMsg.SECKILL_TIME_OVER);
//        }

        // 4.校验是否存在该商品
        Object voStr = redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_PRODUCT_LIST.getRealKey(time + ""), seckillId.toString());
        if (voStr == null){
            // 若这里为空,说明用户恶意测试返问
            throw new BusinessException(CommonCodeMsg.ILLEGAL_OPERATION);
        }
        //SeckillProductVo vo = JSON.parseObject(voStr + "", SeckillProductVo.class);

        // 查看map中是否有该 seckllId,若有,说明其库存为0,直接抛出异常返回
        Boolean isStockUseOutFlag = IS_USE_OUT.get(seckillId);
        if (isStockUseOutFlag != null && isStockUseOutFlag){
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }


        // redis中的库存减1 => 好处: 避免高并发的情况下,已经没有库存了,但每次请求都会返问数据库,性能差, 将请求交由reids接受,提高效率
        // 缺点: 剩余的并发请求都落到了 redis 中, 怎么解决?看上边 添加了 concurrenthashmap 作为本地缓存,如果没有库存,直接返回false
        Long stockCount = redisTemplate.opsForHash().increment(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time + ""), seckillId.toString(), -1);
        System.out.println("stockCount = " + stockCount);
        //int stockCount = (int) redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time + ""), seckillId);
        if (stockCount <= 0){
            IS_USE_OUT.put(seckillId,true);    // 将该秒杀商品的id 和 没有库存的状态存入map中
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER); // "您来晚了，商品已经被抢购完毕."
        }



        // 6.判断用户是否重复下单
        /**
         *  key的设计
         *  zset结构: seckill_order_set:time value: seckillId:phone, sismember: ""
         */
        String token = request.getHeader("token");
        UserInfo user = UserUtil.getUser(redisTemplate,token);
        Boolean flag = redisTemplate.opsForSet().isMember(SeckillRedisKey.SECKILL_ORDER_ZSET.getRealKey(time + ""), seckillId + ":" + user.getPhone());
        if (flag){
            // 回补预库存
            redisTemplate.opsForHash().increment(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time+""),seckillId.toString(),1);
            throw new BusinessException(SeckillCodeMsg.REPEAT_SECKILL); // 请勿重复下单
        }
        // 7.执行秒杀业务
        // String orderNum = orderInfoService.doSeckill(seckillId,time,user.getPhone()); // => 使用rocketMQ优化

        // 使用 rocetMQ 发送消息,让消费者接收后执行秒杀业务
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setSeckillId(seckillId);
        orderMessage.setToken(token);
        orderMessage.setTime(time);
        orderMessage.setUserPhone(user.getPhone());


        rocketMQTemplate.sendOneWay(MQConstant.ORDER_PEDDING_TOPIC,orderMessage);
        return Result.success("正在秒杀,请稍等...");
    }
}
